﻿using System;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

namespace UFrame
{
    /// <summary>
    /// 全局的携程提供
    /// </summary>
    public class CoroutineTool : MonoBehaviour
    {
        private static CoroutineTool instance = null;

        public Action onUpdate;

        public LinkedList<PLResourceRequest> reList =
            new LinkedList<PLResourceRequest>();

        private List<PLResourceRequest> removeList =
            new List<PLResourceRequest>();

        private List<int> listPreRemove = new List<int>();
        private Dictionary<int, WaitBundle> dicWaitBundle = new Dictionary<int, WaitBundle>();
        private Dictionary<int, WaitBundle> dicWaitTempBundle = new Dictionary<int, WaitBundle>();

        public static CoroutineTool I
        {
            get
            {
                if (instance == null)
                {
                    GameObject go = new GameObject("CoroutineProvider");
                    instance = go.AddComponent<CoroutineTool>();

                    DontDestroyOnLoad(instance);
                }

                return instance;
            }
        }

        public void AddPLResourceRequest(PLResourceRequest data)
        {
            reList.AddLast(data);
        }

        public void Update()
        {
            if (onUpdate != null)
            {
                onUpdate();
            }

            UpdateWaitSus();
        }

        private void FixedUpdate()
        {
            if (reList.Count > 0)
            {
                foreach (var item in reList)
                {
                    if (item.request.isDone)
                    {
                        removeList.Add(item);
                    }
                }

                if (removeList.Count > 0)
                {
                    foreach (var removeItem in removeList)
                    {
                        reList.Remove(removeItem);
                        removeItem.onComplete(removeItem.request.asset);
                    }

                    removeList.Clear();
                }
            }
        }


        /// <summary>
        /// 同时添加两个相同的方法,第二个将不会生效
        /// </summary>
        public void AddOnlyWait(Action onComplete, float awaitTime, bool isOver = false)
        {
            int uId = onComplete.GetHashCode();
            bool isHas = false;
            if (dicWaitBundle.ContainsKey(uId))
            {
                dicWaitBundle[uId].curTime = 0;
                isHas = true;
            }

            if (dicWaitTempBundle.ContainsKey(uId))
            {
                dicWaitTempBundle[uId].curTime = 0;
                isHas = true;
            }

            if (isHas)
            {
                WaitBundle wait = new WaitBundle();
                wait.uuid = uId;
                wait.onCompleted = onComplete;
                wait.awaitTime = awaitTime;
                wait.type = 0;
                dicWaitTempBundle.Add(uId, wait);
            }
        }

        /// <summary>
        /// 同时添加两个相同的方法,第二个不会被覆盖
        /// </summary>
        public void AddWait(Action onComplete, float awaitTime)
        {
            int uId = onComplete.GetHashCode();
            do
            {
                uId = uId + Random.Range(0, 10000);
            } while (dicWaitBundle.ContainsKey(uId) || dicWaitTempBundle.ContainsKey(uId));

            WaitBundle wait = new WaitBundle();
            wait.uuid = uId;
            wait.onCompleted = onComplete;
            wait.awaitTime = awaitTime;
            wait.type = 0;
            dicWaitTempBundle.Add(uId, wait);
        }

        public void AddWait(int uuid, Action onComplete, Action onFailed, Func<bool> condition, float maxTime)
        {
            if (dicWaitTempBundle.ContainsKey(uuid))
            {
                return;
            }

            dicWaitTempBundle.Add(uuid, new WaitBundle(uuid, onComplete, onFailed, condition, maxTime));
        }

        private void UpdateWaitSus()
        {
            if (dicWaitTempBundle.Count > 0)
            {
                foreach (var item in dicWaitTempBundle)
                {
                    dicWaitBundle.Add(item.Key, item.Value);
                }

                dicWaitTempBundle.Clear();
            }

            if (dicWaitBundle.Count == 0)
            {
                return;
            }

            foreach (var v in dicWaitBundle.Values)
            {
                if (v.type == 0)
                {
                    v.awaitTime -= Time.deltaTime;
                    if (v.awaitTime <= 0)
                    {
                        listPreRemove.Add(v.uuid);
                        v.onCompleted();
                    }
                }
                else
                {
                    v.curTime -= Time.deltaTime;

                    if (v.condition != null && v.condition())
                    {
                        if (v.onCompleted != null)
                        {
                            v.onCompleted();
                        }

                        listPreRemove.Add(v.uuid);
                        continue;
                    }

                    if (v.curTime <= 0f)
                    {
                        if (v.onFailed != null)
                        {
                            v.onFailed();
                        }

                        listPreRemove.Add(v.uuid);
                    }
                }
            }

            if (listPreRemove.Count > 0)
            {
                foreach (var id in listPreRemove)
                {
                    if (dicWaitBundle.ContainsKey(id))
                    {
                        dicWaitBundle.Remove(id);
                    }
                }

                listPreRemove.Clear();
            }
        }
    }


    /// <summary>
    /// 等待的操作
    /// </summary>
    public sealed class WaitBundle
    {
        public WaitBundle(int uuid, Action onCompleted, Action onFailed, Func<bool> condition, float time)
        {
            this.uuid = uuid;
            this.onCompleted = onCompleted;
            this.onFailed = onFailed;
            this.condition = condition;
            this.curTime = time;
            this.type = 1;
        }

        public WaitBundle()
        {
        }

        public int uuid;
        public Action onCompleted;
        public Action onFailed;
        public Func<bool> condition;
        public float curTime;
        public float awaitTime;
        public int type; // 0 代表时间调用  1代表持续判断
    }
}